from fastapi import Request
import random
import string
from datetime import datetime, timedelta
from typing import Optional, Dict
from jose import jwt
from PIL import Image, ImageDraw, ImageFont
import io
import base64
import uuid
from redis import Redis
from config import settings
from passlib.hash import bcrypt

from model.sysUserModel import SysUser
from service.sysUserService import SysUserService
from service.sysLogininforService import SysLogininforService
from schema.sysLogininforSchema import SysLogininforCreate

# 创建日志服务实例
login_log_service = SysLogininforService()

# 使用配置中的所有Redis参数创建连接
redis_client = Redis(
    host=settings.REDIS_HOST,
    port=settings.REDIS_PORT,
    db=settings.REDIS_DB,
    password=settings.REDIS_PASSWORD  # 添加密码
)


class CommonService:
    @staticmethod
    async def encrypt_password(username: str, password: str) -> Optional[SysUser]:
        """验证用户密码并返回用户信息"""
        user_service = SysUserService()
        user = await user_service.get_user_by_username(username)
        if not user:
            return None

        # 使用 passlib 的 bcrypt 验证密码
        if bcrypt.verify(password, user.password):
            return user

        return None

    @staticmethod
    def generate_code(length=4):
        """生成随机验证码"""
        characters = string.ascii_uppercase + string.digits
        return ''.join(random.choice(characters) for _ in range(length))

    @staticmethod
    def generate_captcha_image(code):
        """生成验证码图片"""
        width = 130
        height = 48
        # 创建图片对象
        image = Image.new('RGB', (width, height), 'white')
        draw = ImageDraw.Draw(image)

        # 使用简单的字体
        try:
            font = ImageFont.truetype('arial.ttf', 36)
        except:
            font = ImageFont.load_default()

        # 绘制字符
        for i, char in enumerate(code):
            x = 20 + i * 25
            y = random.randint(2, 8)
            draw.text((x, y), char, font=font, fill='black')

        # 添加干扰线
        for _ in range(4):
            x1 = random.randint(0, width)
            y1 = random.randint(0, height)
            x2 = random.randint(0, width)
            y2 = random.randint(0, height)
            draw.line([(x1, y1), (x2, y2)], fill='gray')

        # 添加噪点
        for _ in range(50):
            x = random.randint(0, width)
            y = random.randint(0, height)
            draw.point((x, y), fill='gray')

        # 转换为base64
        buffer = io.BytesIO()
        image.save(buffer, format='PNG')
        img_str = base64.b64encode(buffer.getvalue()).decode()

        # return f"data:image/png;base64,{img_str}"  # 添加data URI前缀
        return img_str

    @staticmethod
    def save_captcha(code):
        """保存验证码到Redis"""
        uuid_key = str(uuid.uuid4())
        try:
            redis_client.setex(f"captcha:{uuid_key}", 300, code)  # 5分钟过期
            return uuid_key
        except Exception as e:
            # 可以添加日志记录
            print(f"Redis error: {str(e)}")
            raise Exception("验证码服务暂时不可用")

    @staticmethod
    def verify_captcha(uuid_key, code):
        """验证验证码"""
        try:
            stored_code = redis_client.get(f"captcha:{uuid_key}")
            if not stored_code:
                return False
            # 验证成功后删除验证码
            redis_client.delete(f"captcha:{uuid_key}")
            return stored_code.decode().upper() == code.upper()
        except Exception as e:
            # 可以添加日志记录
            print(f"Redis error: {str(e)}")
            return False

    @staticmethod
    def datetime_to_string(dt: datetime) -> str:
        """转换datetime为字符串格式"""
        if dt:
            return dt.strftime("%Y-%m-%d %H:%M:%S")
        return None

    @staticmethod
    def process_user_dict(user_dict: dict) -> dict:
        """处理用户字典中的datetime类型"""
        # 转换所有datetime类型的字段
        for key, value in user_dict.items():
            if isinstance(value, datetime):
                user_dict[key] = CommonService.datetime_to_string(value)
        return user_dict

    async def login(self, username: str, password: str, code: str, uuid_key: str, request: Request) -> Dict:
        """
        处理用户登录逻辑
        返回: 包含token和用户信息的字典,或者错误信息
        """

        client_info = self.get_client_info(request)

        log_data = SysLogininforCreate(
            user_name=username,
            ipaddr=client_info.get("ipaddr", "unknown"),
            login_location=client_info.get("login_location", "unknown"),
            browser=client_info.get("browser", "unknown"),
            os=client_info.get("os", "unknown"),
            login_time=datetime.now()
        )

        # 验证验证码
        if not CommonService.verify_captcha(uuid_key, code):
            # 记录验证码错误日志
            log_data.login_status = "1"
            log_data.msg = "验证码错误或已过期"
            await login_log_service.create(log_data, username)

            return {
                "code": 201,
                "msg": "验证码错误或已过期"
            }

        # 验证密码
        user = await CommonService.encrypt_password(username, password)
        if user is None:
            # 记录密码错误日志
            log_data.login_status = "1"
            log_data.msg = "用户名密码错误"
            await login_log_service.create(log_data, username)

            return {
                "code": 201,
                "msg": "用户名密码错误"
            }

        # 验证用户状态
        if user.use_flag != "0":
            # 记录用户禁用日志
            log_data.login_status = "1"
            log_data.msg = "用户已被禁用"
            await login_log_service.create(log_data, username)

            return {
                "code": 201,
                "msg": "用户已被禁用"
            }

        # 生成token
        token = CommonService.generate_token(user)

        # 记录登录成功日志
        log_data.login_status = "0"  # 成功
        log_data.msg = "登录成功"
        await login_log_service.create(log_data, username)

        # 准备返回用户信息（去除敏感信息）
        user_info = user.dict()
        user_info.pop("password", None)
        # 处理datetime字段
        user_info = CommonService.process_user_dict(user_info)

        return {
            "code": 200,
            "msg": "登录成功",
            "data": {
                "userInfo": user_info,
                "username": user_info['user_name'],
                "token": token
            }
        }

    @staticmethod
    def generate_token(user: SysUser) -> str:
        """
        使用python-jose生成JWT token
        """
        # token过期时间设置为24小时
        expire = datetime.utcnow() + timedelta(hours=24)

        # 准备token数据
        token_data = {
            "user_id": user.user_id,
            "username": user.user_name,
            "exp": expire.timestamp()  # python-jose需要使用timestamp
        }

        # 使用jose.jwt生成token
        token = jwt.encode(
            token_data,
            settings.SECRET_KEY,  # 需要在配置中添加密钥
            algorithm="HS256"
        )

        return token

    async def logout_token(self, token: str, request: Request) -> bool:
        """
        将token加入黑名单
        Args:
            :param token:
            :param request:
        Returns:
            bool: 操作是否成功
        """
        try:
            current_user = request.state.user
            user_name = current_user["username"]

            client_info = self.get_client_info(request)

            # 解析token获取过期时间
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
            exp_timestamp = payload.get('exp')

            if exp_timestamp:
                # 计算剩余有效期（秒）
                current_timestamp = datetime.utcnow().timestamp()
                ttl = int(exp_timestamp - current_timestamp)

                if ttl > 0:
                    # 将token加入黑名单，过期时间与token原过期时间一致
                    redis_client.setex(f"token_blacklist:{token}", ttl, "1")

                    # 记录登出日志
                    log_data = SysLogininforCreate(
                        user_name=user_name,
                        ipaddr=client_info.get("ipaddr", "unknown"),
                        login_location=client_info.get("login_location", "unknown"),
                        browser=client_info.get("browser", "unknown"),
                        os=client_info.get("os", "unknown"),
                        login_status="0",
                        msg="退出成功",
                        login_time=datetime.now()
                    )
                    await login_log_service.create(log_data, user_name)

                    return True
            return False
        except Exception as e:
            print(f"Token logout error: {str(e)}")
            return False

    @staticmethod
    def is_token_blacklisted(token: str) -> bool:
        """
        检查token是否在黑名单中
        Args:
            token: 用户的JWT token
        Returns:
            bool: 是否在黑名单中
        """
        try:
            return bool(redis_client.exists(f"token_blacklist:{token}"))
        except Exception as e:
            print(f"Token blacklist check error: {str(e)}")
            return False

    @staticmethod
    def get_client_info(request: Request) -> dict:
        """获取客户端信息"""
        # 获取客户端IP
        forwarded = request.headers.get("X-Forwarded-For")
        if forwarded:
            ip = forwarded.split(",")[0].strip()
        else:
            ip = request.client.host if request.client else "127.0.0.1"

        # 获取User-Agent信息
        user_agent = request.headers.get("User-Agent", "")

        # 简单解析User-Agent获取浏览器和操作系统信息
        browser = "Unknown"
        os = "Unknown"

        if "Chrome" in user_agent:
            browser = "Chrome"
        elif "Firefox" in user_agent:
            browser = "Firefox"
        elif "Safari" in user_agent:
            browser = "Safari"
        elif "Edge" in user_agent:
            browser = "Edge"
        elif "MSIE" in user_agent or "Trident" in user_agent:
            browser = "IE"

        if "Windows" in user_agent:
            os = "Windows"
        elif "Mac OS" in user_agent:
            os = "Mac OS"
        elif "Linux" in user_agent:
            os = "Linux"
        elif "Android" in user_agent:
            os = "Android"
        elif "iPhone" in user_agent or "iPad" in user_agent:
            os = "iOS"

        return {
            "ipaddr": ip,
            "browser": browser,
            "os": os,
            "login_location": "内网IP" if ip.startswith(("127.", "192.168.", "10.")) else "外网IP"  # 这里可以集成IP地理位置服务
        }
